#include <iostream>

#include <nemesis.h>
#include <nemesis.dbms.h>
#include <nemesis.app.h>

#include <nemesis.dbms.oracle.Database.h>

class TestDate : public Application {
public:
   TestDate ();
   
private:
   nemesis::dbms::Database* a_db;

   void initialize () throw (RuntimeException); 
   void run () throw (RuntimeException);      
   
   void insert (dbms::Connection* connection) throw (RuntimeException);
};

using namespace std;

int main (int argc, const char** argv)
{
   CommandLine& commandLine (CommandLine::instanciate ());
   TestDate testNull;

   try {
      commandLine.initialize (argv, argc);
      commandLine.verify ();

      Logger::setLevel (Logger::Debug); 
      Logger::initialize ("copy", new TraceWriter ("db_date.trace", 1024 * 1024));
 
      testNull.start ();
   }
   catch (Exception& ex) {
      cout << ex.asString () << endl;
   }
   
   return 0;
}

TestDate::TestDate () : 
   Application ("db_date", "Prueba de los nuevos tipos dbms::Date y dbms::TimeStamp", "1.0") 
{
   CommandLine& cl (CommandLine::instanciate ());
      
   cl.add ("user", CommandLine::Argument::Mandatory, "Nombre del usuario");
   cl.add ("password", CommandLine::Argument::Mandatory, "Clave del usuario");
   cl.add ("db", CommandLine::Argument::Optional, "Nombre de la base de datos");
}

void TestDate::initialize ()
   throw (RuntimeException)
{
   a_db = new nemesis::dbms::oracle::Database (CommandLine::instanciate ().getValue ("db"));   
}

void TestDate::run () 
   throw (RuntimeException)
{
   LOGMETHOD (TraceMethod tm ("TestDate", "run", FILE_LOCATION)); 

   CommandLine& cl (CommandLine::instanciate ());
   
   Statement* create;
   Statement* select;
  
   dbms::Integer n;
   dbms::Date date01 (true);
   dbms::TimeStamp time01 (true);

   dbms::ResultCode resultCode;

   try {      
      Connection* connection = a_db->createConnection ("db_date", cl.getValue ("user"), cl.getValue ("password"));

      {
         Guard guard (connection);

         try {
            create = a_db->createStatement ("create", "create table nemesis_date (xx integer not null, yy date, zz timestamp)");
            resultCode = connection->execute (create);
            cout << "Creando: " << resultCode.asString () << endl << endl;
         }
         catch (Exception& ex) {
            ex.trace ();
         }
      }
      
      insert (connection);

      Guard guard (connection);
      
      // Observar que el formato indicado en el to_char debe coincidir con la especificacion dada
      // en el constructor dela output-bind.  
      select = a_db->createStatement ("select", "select xx,yy,zz from nemesis_date order by 1");
      select->bindOutput ("XX", n);
      select->bindOutput ("YY", date01);
      select->bindOutput ("zz", time01);

      cout << endl << " --- Leyendo sin formato ---" << endl;
      resultCode = connection->execute (select);
                        
      if (resultCode.successful () == true) {
         cout << "ID" <<  "\t" << "Date\t\t\t" << "Date2\t\t" << "TimeStamp" << endl;
         while (select->fetch () == true) {
            cout << n << "\t";            

            if (date01.isNull () == true) { 
               cout << "<null>\t\t\t<null>\t\t";
            }
            else {  // Ejemplo de las dos formas de acceder a los componentes de dbms::Date/dbms::TimeStamp
               cout << date01.getCStringValue () << "\t";
               cout << date01.getHour () << ':' << date01.getMinute () << ':' << date01.getSecond () << "\t\t";
            }            
            
            cout << ((time01.isNull () == true) ? "<null>": time01.getCStringValue ()) <<  endl;
         }         
      }
      else
         cout << resultCode.asString () << endl << endl;
      
      cout << endl << " --- Leyendo con formato ---" << endl;
      dbms::Date date02 (true, "%d-%B-%y  %H:%M:%S");
      dbms::TimeStamp time02 (true, "%c.%%d");     // "%%d" Truco para sacar la parte fraccionaria del TimeStamp
      Statement* select2;
      
      select2 = a_db->createStatement ("select2","select xx,yy,zz from nemesis_date order by 1" );
      select2->bindOutput ("XX", n);
      select2->bindOutput ("YY", date02);
      select2->bindOutput ("zz", time02);

      resultCode = connection->execute (select2);
                        
      if (resultCode.successful () == true) {
         cout << "ID" <<  "\t" << "Date" <<  "\t\t\t\t" << "TimeStamp" << endl;
         while (select2->fetch () == true) {
            cout << n << "\t";            
            cout << ((date02.isNull () == true) ? "<null>\t\t": date02.getCStringValue ()) <<  "\t\t";
            cout << ((time02.isNull () == true) ? "<null>": time02.getCStringValue ()) <<  endl;            
         }         
      }
      else
         cout << resultCode.asString () << endl << endl;      
   }
   catch (dbms::DatabaseException& edb) {
      throw RuntimeException (edb); 
   }

   writeContext ("db_date.context");
}

void TestDate::insert (dbms::Connection* connection)
   throw (RuntimeException)
{
   Statement* insert;
   dbms::Integer n;
   dbms::Date date01 (true);
   dbms::TimeStamp time01 (true);
   dbms::ResultCode resultCode;

   Guard guard (connection);

   insert = a_db->createStatement ("insert", "insert into nemesis_date (xx, yy, zz) values (&x, &y, &z)");  
   insert->bindInput ("XX", n);
   insert->bindInput ("YY", date01);
   insert->bindInput ("zz", time01);

   cout << " --- Grabando ---" << endl;
   n = 1;
   date01.setValue (nemesis::functions::second ());
   time01 = date01;
   resultCode = connection->execute (insert);
   cout << resultCode.asString () << endl << endl;
                     
   n = 2;
   date01.setNull (true);
   time01.setYear (2020);
   time01.setFractionalSecond (125);
   resultCode = connection->execute (insert);
   cout << resultCode.asString () << endl << endl;

   n = 3;
   date01.setYear (2009);
   date01.setMonth (9);
   date01.setDay (9);
   date01.setHour (20);
   date01.setMinute (9);
   date01.setSecond (9);
   time01.setNull (true);
   resultCode = connection->execute (insert);
   cout << resultCode.asString () << endl << endl;

   try { // Solo funcionaria en caso de indicar un formato de especificacion en el constructor.
      n = 4;
      date01.setValue ("2007/12/31 22:50");
      time01.setValue ( "2008/01/30");
      resultCode = connection->execute (insert);
      cout << resultCode.asString () << endl << endl;         
   }
   catch (Exception& ex) {
      ex.trace ();
   }
   
   n = 5;
   date01.setNull (true);
   time01.setYear (2005);
   time01.setMonth (5);
   time01.setDay (5);
   time01.setHour (5);
   time01.setMinute (5);
   time01.setFractionalSecond (55000);
   resultCode = connection->execute (insert);
   cout << resultCode.asString () << endl << endl;   
}

